home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 1 (Walnut Creek)
/
Aminet - June 1993 [Walnut Creek].iso
/
usenet
/
sources
/
volume90
/
unix
/
cpp
/
part03
/
Cpp4.c
next >
Wrap
C/C++ Source or Header
|
1990-01-18
|
20KB
|
676 lines
/*
* C P P 4 . C
* M a c r o D e f i n i t i o n s
*
* Edit History
* 31-Aug-84 MM USENET net.sources release
* 04-Oct-84 MM __LINE__ and __FILE__ must call ungetstring()
* so they work correctly with token concatenation.
* Added string formal recognition.
* 25-Oct-84 MM "Short-circuit" evaluate #if's so that we
* don't print unnecessary error messages for
* #if !defined(FOO) && FOO != 0 && 10 / FOO ...
* 31-Oct-84 ado/MM Added token concatenation
* 6-Nov-84 MM Split off eval stuff
* 21-Oct-85 RMS Rename `token' to `tokenbuf'.
* In doundef, don't complain if arg already not defined.
* 14-Mar-86 FNF Incorporate macro based C debugging package.
* Port to Commodore AMIGA.
* 21-Aug-88 Ois Changed concatenation operator to ##. Changed hand-
* ling of tokens following ##. Added new meaning of #.
*/
#include <stdio.h>
#include <ctype.h>
#include "cppdef.h"
#include "cpp.h"
/*
* parm[], parmp, and parlist[] are used to store #define() argument
* lists. nargs contains the actual number of parameters stored.
*/
static char parm[NPARMWORK + 1]; /* define param work buffer */
static char *parmp; /* Free space in parm */
static char *parlist[LASTPARM]; /* -> start of each parameter */
static int nargs; /* Parameters for this macro */
dodefine()
/*
* Called from control when a #define is scanned. This module
* parses formal parameters and the replacement string. When
* the formal parameter name is encountered in the replacement
* string, it is replaced by a character in the range 128 to
* 128+NPARAM (this allows up to 32 parameters within the
* Dec Multinational range). If cpp is ported to an EBCDIC
* machine, you will have to make other arrangements.
*
* There is some special case code to distinguish
* #define foo bar
* from #define foo() bar
*
* Also, we make sure that
* #define foo foo
* expands to "foo" but doesn't put cpp into an infinite loop.
*
* A warning message is printed if you redefine a symbol to a
* different text. I.e,
* #define foo 123
* #define foo 123
* is ok, but
* #define foo 123
* #define foo +123
* is not.
*
* The following subroutines are called from define():
* checkparm called when a token is scanned. It checks through the
* array of formal parameters. If a match is found, the
* token is replaced by a control byte which will be used
* to locate the parameter when the macro is expanded.
* textput puts a string in the macro work area (parm[]), updating
* parmp to point to the first free byte in parm[].
* textput() tests for work buffer overflow.
* charput puts a single character in the macro work area (parm[])
* in a manner analogous to textput().
*/
{
register int c;
register DEFBUF *dp; /* -> new definition */
int isredefine; /* TRUE if redefined */
char *old; /* Remember redefined */
#if OK_CONCAT
register int quoting; /* Remember we saw a # */
#endif
extern int save(); /* Save char in work[] */
DBUG_ENTER ("dodefine");
if (type[(c = skipws())] != LET)
goto bad_define;
isredefine = FALSE; /* Set if redefining */
if ((dp = lookid(c)) == NULL) /* If not known now */
dp = defendel(tokenbuf, FALSE); /* Save the name */
else { /* It's known: */
isredefine = TRUE; /* Remember this fact */
old = dp->repl; /* Remember replacement */
dp->repl = NULL; /* No replacement now */
}
parlist[0] = parmp = parm; /* Setup parm buffer */
if ((c = get()) == '(') { /* With arguments? */
nargs = 0; /* Init formals counter */
do { /* Collect formal parms */
if (nargs >= LASTPARM)
cfatal("Too many arguments for macro", NULLST);
else if ((c = skipws()) == ')')
break; /* Got them all */
else if (type[c] != LET) /* Bad formal syntax */
goto bad_define;
scanid(c); /* Get the formal param */
parlist[nargs++] = parmp; /* Save its start */
textput(tokenbuf); /* Save text in parm[] */
} while ((c = skipws()) == ','); /* Get another argument */
if (c != ')') /* Must end at ) */
goto bad_define;
c = ' '; /* Will skip to body */
}
else {
/*
* DEF_NOARGS is needed to distinguish between
* "#define foo" and "#define foo()".
*/
nargs = DEF_NOARGS; /* No () parameters */
}
if (type[c] == SPA) /* At whitespace? */
c = skipws(); /* Not any more. */
workp = work; /* Replacement put here */
inmacro = TRUE; /* Keep \<newline> now */
quoting = 0; /* No # seen yet. */
while (c != EOF_CHAR && c != '\n') { /* Compile macro body */
#if OK_CONCAT
if (c == '#') { /* Token concatenation? */
if ((c = get()) != '#') { /* No, not really */
quoting = 1; /* Maybe quoting op. */
continue;
}
while (workp > work && type[workp[-1]] == SPA)
--workp; /* Erase leading spaces */
save(TOK_SEP); /* Stuff a delimiter */
c = skipws(); /* Eat whitespace */
#if 0
if (type[c] == LET) /* Another token here? */
; /* Stuff it normally */
else if (type[c] == DIG) { /* Digit string after? */
while (type[c] == DIG) { /* Stuff the digits */
save(c); /* Note
c = get();
}
save(TOK_SEP); /* Delimit 2nd token */
}
else {
ciwarn("Strange character after ## (%d.)", c);
}
#endif
continue;
}
#endif
switch (type[c]) {
case LET:
#if OK_CONCAT
checkparm(c, dp, quoting); /* Might be a formal */
#else
checkparm(c, dp); /* Might be a formal */
#endif
break;
case DIG: /* Number in mac. body */
case DOT: /* Maybe a float number */
scannumber(c, save); /* Scan it off */
break;
case QUO: /* String in mac. body */
#if STRING_FORMAL
stparmscan(c, dp); /* Do string magic */
#else
stparmscan(c);
#endif
break;
case BSH: /* Backslash */
save('\\');
if ((c = get()) == '\n')
wrongline = TRUE;
save(c);
break;
case SPA: /* Absorb whitespace */
/*
* Note: the "end of comment" marker is passed on
* to allow comments to separate tokens.
*/
if (workp[-1] == ' ') /* Absorb multiple */
break; /* spaces */
else if (c == '\t')
c = ' '; /* Normalize tabs */
/* Fall through to store character */
default: /* Other character */
save(c);
break;
}
c = get();
quoting = 0; /* Only when immediately*/
/* preceding a formal */
}
inmacro = FALSE; /* Stop newline hack */
unget(); /* For control check */
if (workp > work && workp[-1] == ' ') /* Drop trailing blank */
workp--;
*workp = EOS; /* Terminate work */
dp->repl = savestring(work); /* Save the string */
dp->nargs = nargs; /* Save arg count */
#if DEBUG
if (debug)
dumpadef("macro definition", dp);
#endif
if (isredefine) { /* Error if redefined */
if ((old != NULL && dp->repl != NULL && !streq(old, dp->repl))
|| (old == NULL && dp->repl != NULL)
|| (old != NULL && dp->repl == NULL)) {
cerror("Redefining defined variable \"%s\"", dp->name);
}
if (old != NULL) /* We don't need the */
free(old); /* old definition now. */
}
DBUG_VOID_RETURN;
bad_define:
cerror("#define syntax error", NULLST);
inmacro = FALSE; /* Stop <newline> hack */
DBUG_VOID_RETURN;
}
checkparm(c, dp, quoting)
register int c;
DEFBUF *dp;
int quoting; /* Preceded by a # ? */
/*
* Replace this param if it's defined. Note that the macro name is a
* possible replacement token. We stuff DEF_MAGIC in front of the token
* which is treated as a LETTER by the token scanner and eaten by
* the output routine. This prevents the macro expander from
* looping if someone writes "#define foo foo".
*/
{
register int i;
register char *cp;
DBUG_ENTER ("checkparm");
scanid(c); /* Get parm to tokenbuf */
for (i = 0; i < nargs; i++) { /* For each argument */
if (streq(parlist[i], tokenbuf)) { /* If it's known */
#if OK_CONCAT
if (quoting) /* Special handling of */
save(QUOTE_PARM); /* #formal inside defn */
#endif
save(i + MAC_PARM); /* Save a magic cookie */
DBUG_VOID_RETURN; /* And exit the search */
}
}
if (streq(dp->name, tokenbuf)) /* Macro name in body? */
save(DEF_MAGIC); /* Save magic marker */
for (cp = tokenbuf; *cp != EOS;) /* And save */
save(*cp++); /* The token itself */
DBUG_VOID_RETURN;
}
#if STRING_FORMAL
stparmscan(delim, dp)
int delim;
register DEFBUF *dp;
/*
* Scan the string (starting with the given delimiter).
* The token is replaced if it is the only text in this string or
* character constant. The algorithm follows checkparm() above.
* Note that scanstring() has approved of the string.
*/
{
register int c;
DBUG_ENTER ("stparmscan");
/*
* Warning -- this code hasn't been tested for a while.
* It exists only to preserve compatibility with earlier
* implementations of cpp. It is not part of the Draft
* ANSI Standard C language.
*/
save(delim);
instring = TRUE;
while ((c = get()) != delim
&& c != '\n'
&& c != EOF_CHAR) {
if (type[c] == LET) /* Maybe formal parm */
checkparm(c, dp, 0);
else {
save(c);
if (c == '\\')
save(get());
}
}
instring = FALSE;
if (c != delim)
cerror("Unterminated string in macro body", NULLST);
save(c);
DBUG_VOID_RETURN;
}
#else
stparmscan(delim)
int delim;
/*
* Normal string parameter scan.
*/
{
register char *wp;
register int i;
extern int save();
DBUG_ENTER ("stparmscan");
wp = workp; /* Here's where it starts */
if (!scanstring(delim, save))
DBUG_VOID_RETURN; /* Exit on scanstring error */
workp[-1] = EOS; /* Erase trailing quote */
wp++; /* -> first string content byte */
for (i = 0; i < nargs; i++) {
if (streq(parlist[i], wp)) {
*wp++ = MAC_PARM + PAR_MAC; /* Stuff a magic marker */
*wp++ = (i + MAC_PARM); /* Make a formal marker */
*wp = wp[-3]; /* Add on closing quote */
workp = wp + 1; /* Reset string end */
DBUG_VOID_RETURN;
}
}
workp[-1] = wp[-1]; /* Nope, reset end quote. */
DBUG_VOID_RETURN;
}
#endif
doundef()
/*
* Remove the symbol from the defined list.
* Called from the #control processor.
*/
{
register int c;
DBUG_ENTER ("doundef");
if (type[(c = skipws())] != LET)
cerror("Illegal #undef argument", NULLST);
else
{
scanid(c); /* Get name to tokenbuf */
(void) defendel(tokenbuf, TRUE);
}
DBUG_VOID_RETURN;
}
textput(text)
char *text;
/*
* Put the string in the parm[] buffer.
*/
{
register int size;
DBUG_ENTER ("textput");
size = strlen(text) + 1;
if ((parmp + size) >= &parm[NPARMWORK])
cfatal("Macro work area overflow", NULLST);
else {
strcpy(parmp, text);
parmp += size;
}
DBUG_VOID_RETURN;
}
charput(c)
register int c;
/*
* Put the byte in the parm[] buffer.
*/
{
if (parmp >= &parm[NPARMWORK])
cfatal("Macro work area overflow", NULLST);
else {
*parmp++ = c;
}
}
/*
* M a c r o E x p a n s i o n
*/
static DEFBUF *macro; /* Catches start of infinite macro */
expand(tokenp)
register DEFBUF *tokenp;
/*
* Expand a macro. Called from the cpp mainline routine (via subroutine
* macroid()) when a token is found in the symbol table. It calls
* expcollect() to parse actual parameters, checking for the correct number.
* It then creates a "file" containing a single line containing the
* macro with actual parameters inserted appropriately. This is
* "pushed back" onto the input stream. (When the get() routine runs
* off the end of the macro line, it will dismiss the macro itself.)
*/
{
register int c;
register FILEINFO *file;
extern FILEINFO *getfile();
DBUG_ENTER ("expand");
#if DEBUG
if (debug)
dumpadef("expand entry", tokenp);
#endif
/*
* If no macro is pending, save the name of this macro
* for an eventual error message.
*/
if (recursion++ == 0)
macro = tokenp;
else if (recursion == RECURSION_LIMIT) {
cerror("Recursive macro definition of \"%s\"", tokenp->name);
fprintf(stderr, "(Defined by \"%s\")\n", macro->name);
if (rec_recover) {
do {
c = get();
} while (infile != NULL && infile->fp == NULL);
unget();
recursion = 0;
DBUG_VOID_RETURN;
}
}
/*
* Here's a macro to expand.
*/
nargs = 0; /* Formals counter */
parmp = parm; /* Setup parm buffer */
switch (tokenp->nargs) {
case (-2): /* __LINE__ */
sprintf(work, "%d", line);
ungetstring(work);
break;
case (-3): /* __FILE__ */
for (file = infile; file != NULL; file = file->parent) {
if (file->fp != NULL) {
sprintf(work, "\"%s\"", (file->progname != NULL)
? file->progname : file->filename);
ungetstring(work);
break;
}
}
break;
default:
/*
* Nothing funny about this macro.
*/
if (tokenp->nargs < 0)
cfatal("Bug: Illegal __ macro \"%s\"", tokenp->name);
while ((c = skipws()) == '\n') /* Look for (, skipping */
wrongline = TRUE; /* spaces and newlines */
if (c != '(') {
/*
* If the programmer writes
* #define foo() ...
* ...
* foo [no ()]
* just write foo to the output stream.
*/
unget();
cwarn("Macro \"%s\" needs arguments", tokenp->name);
fputs(tokenp->name, stdout);
DBUG_VOID_RETURN;
}
else if (expcollect()) { /* Collect arguments */
if (tokenp->nargs != nargs) { /* Should be an error? */
cwarn("Wrong number of macro arguments for \"%s\"",
tokenp->name);
}
#if DEBUG
if (debug)
dumpparm("expand");
#endif
} /* Collect arguments */
case DEF_NOARGS: /* No parameters just stuffs */
expstuff(tokenp); /* Do actual parameters */
} /* nargs switch */
DBUG_VOID_RETURN;
}
FILE_LOCAL int
expcollect()
/*
* Collect the actual parameters for this macro. TRUE if ok.
*/
{
register int c;
register int paren; /* For embedded ()'s */
extern int charput();
DBUG_ENTER ("expcollect");
for (;;) {
paren = 0; /* Collect next arg. */
while ((c = skipws()) == '\n') /* Skip over whitespace */
wrongline = TRUE; /* and newlines. */
if (c == ')') { /* At end of all args? */
/*
* Note that there is a guard byte in parm[]
* so we don't have to check for overflow here.
*/
*parmp = EOS; /* Make sure terminated */
break; /* Exit collection loop */
}
else if (nargs >= LASTPARM)
cfatal("Too many arguments in macro expansion", NULLST);
parlist[nargs++] = parmp; /* At start of new arg */
for (;; c = cget()) { /* Collect arg's bytes */
if (c == EOF_CHAR) {
cerror("end of file within macro argument", NULLST);
DBUG_RETURN (FALSE); /* Sorry. */
}
else if (c == '\\') { /* Quote next character */
charput(c); /* Save the \ for later */
charput(cget()); /* Save the next char. */
continue; /* And go get another */
}
else if (type[c] == QUO) { /* Start of string? */
scanstring(c, charput); /* Scan it off */
continue; /* Go get next char */
}
else if (c == '(') /* Worry about balance */
paren++; /* To know about commas */
else if (c == ')') { /* Other side too */
if (paren == 0) { /* At the end? */
unget(); /* Look at it later */
break; /* Exit arg getter. */
}
paren--; /* More to come. */
}
else if (c == ',' && paren == 0) /* Comma delimits args */
break;
else if (c == '\n') /* Newline inside arg? */
wrongline = TRUE; /* We'll need a #line */
charput(c); /* Store this one */
} /* Collect an argument */
charput(EOS); /* Terminate argument */
#if DEBUG
if (debug)
printf("parm[%d] = \"%s\"\n", nargs, parlist[nargs - 1]);
#endif
} /* Collect all args. */
DBUG_RETURN (TRUE); /* Normal return */
}
#if OK_CONCAT
FILE_LOCAL
char *doquoting(to, from)
register char *to;
register char *from;
{
*to++ = '"';
while (*from) {
if (*from == '\\' || *from == '"')
*to++ = '\\';
*to++ = *from++;
}
*to++ = '"';
return to;
}
#endif
FILE_LOCAL
expstuff(tokenp)
DEFBUF *tokenp; /* Current macro being expanded */
/*
* Stuff the macro body, replacing formal parameters by actual parameters.
*/
{
register int c; /* Current character */
register char *inp; /* -> repl string */
register char *defp; /* -> macro output buff */
int size; /* Actual parm. size */
char *defend; /* -> output buff end */
int string_magic; /* String formal hack */
FILEINFO *file; /* Funny #include */
#if OK_CONCAT
register char quoting; /* Quote macro argument */
#endif
extern FILEINFO *getfile();
DBUG_ENTER ("expstuff");
file = getfile(NBUFF, tokenp->name);
inp = tokenp->repl; /* -> macro replacement */
defp = file->buffer; /* -> output buffer */
defend = defp + (NBUFF - 1); /* Note its end */
if (inp != NULL) {
quoting = 0;
while ((c = (*inp++ & 0xFF)) != EOS) {
#if OK_CONCAT
if (c == QUOTE_PARM) { /* Special token for # */
quoting = 1; /* set flag, for later */
continue; /* Get next character */
}
#endif
if (c >= MAC_PARM && c <= (MAC_PARM + PAR_MAC)) {
string_magic = (c == (MAC_PARM + PAR_MAC));
if (string_magic)
c = (*inp++ & 0xFF);
/*
* Replace formal parameter by actual parameter string.
*/
if ((c -= MAC_PARM) < nargs) {
size = strlen(parlist[c]);
#if OK_CONCAT
if (quoting) {
size++;
size *= 2; /* worst case condition */
}
#endif
if ((defp + size) >= defend)
goto nospace;
/*
* Erase the extra set of quotes.
*/
if (string_magic && defp[-1] == parlist[c][0]) {
strcpy(defp-1, parlist[c]);
defp += (size - 2);
}
#if OK_CONCAT
else if (quoting)
defp = doquoting(defp, parlist[c]);
#endif
else {
strcpy(defp, parlist[c]);
defp += size;
}
}
}
else if (defp >= defend) {
nospace: cfatal("Out of space in macro \"%s\" arg expansion",
tokenp->name);
}
else {
*defp++ = c;
}
quoting = 0;
}
}
*defp = EOS;
#if DEBUG
if (debug > 1)
printf("macroline: \"%s\"\n", file->buffer);
#endif
DBUG_VOID_RETURN;
}
#if DEBUG
dumpparm(why)
char *why;
/*
* Dump parameter list.
*/
{
register int i;
DBUG_ENTER ("dumpparm");
printf("dump of %d parameters (%d bytes total) %s\n",
nargs, parmp - parm, why);
for (i = 0; i < nargs; i++) {
printf("parm[%d] (%d) = \"%s\"\n",
i + 1, strlen(parlist[i]), parlist[i]);
}
DBUG_VOID_RETURN;
}
#endif